/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.model;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import org.directwebremoting.annotations.DataTransferObject;
import org.directwebremoting.annotations.RemoteProperty;
import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.NotNull;
import org.internna.iwebjtracker.project.model.Module;
import org.internna.iwebmvc.metadata.ui.EntityFilter;
import org.internna.iwebmvc.metadata.ui.GridColumn;
import org.internna.iwebmvc.model.AbstractOwnedDomainEntity;
import org.internna.iwebmvc.model.security.UserImpl;
import org.internna.iwebmvc.utils.CollectionUtils;

import static org.internna.iwebmvc.utils.StringUtils.hasText;
/**
 * A general purpose Module implementation.
 *
 * @author Jose Noheda
 * @since 1.0
 */
@Entity
@Table(name = "MODULES")
@NamedQuery(name = "DefaultModule.findAll", query = "SELECT m FROM DefaultModule m")
@DataTransferObject(javascript = "DefaultModule")
public class DefaultModule extends AbstractOwnedDomainEntity implements Module<DefaultProject, DefaultModule> {

    private static final long serialVersionUID = 2673684528498822417L;

    @NotEmpty
    @RemoteProperty
    @Column(name = "NAME")
    @EntityFilter (order = 2)
    @GridColumn
    private String moduleName;

    @Lob
    @NotEmpty
    @RemoteProperty
    @Column(name = "DESCRIPTION")
    @GridColumn(order = 4, relativeWidth = 2)
    private String description;

    @NotNull
    @ManyToOne
    @RemoteProperty
    @GridColumn(order = 2)
    @JoinColumn(name = "PROJECT", updatable = false, nullable = false)
    @EntityFilter(type = EntityFilter.WIDGET_TYPE.ENTITY, order = 1)
    private DefaultProject parentProject;

    @ManyToOne
    @RemoteProperty
    @GridColumn(order = 3)
    @JoinColumn(name = "MODULE", updatable = false)
    private DefaultModule parentModule;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "parentModule")
    private Set<DefaultModule> components;

    /**
     * Accessor method
     */
    @Override public String getDescription() {
        return description;
    }

    /**
     * Accessor method
     */
    @Override public void setDescription(String description) {
        this.description = description;
    }

    /**
     * Accessor method.
     *
     * @return any
     */
    @Override public DefaultProject getParentProject() {
        return parentProject;
    }

    /**
     * Accessor method. Clears parent module in the process.
     */
    @Override public void setParentProject(DefaultProject parentProject) {
        this.parentProject = parentProject;
        if ((parentModule != null) && (!this.parentModule.getParentProject().equals(this.parentProject))) {
            setParentModule(null);
        }
    }

    /**
     * String representation suitable for UI widgets.
     */
    @Override public final String getCaption(Locale locale) {
        return getModuleName();
    }

    /**
     * Accessor method
     */
    @Override public String getModuleName() {
        return moduleName;
    }

    /**
     * Accessor method
     */
    @Override public void setModuleName(String name) {
        this.moduleName = name;
    }

    /**
     * Accessor method
     */
    @Override public DefaultModule getParentModule() {
        return parentModule;
    }

    /**
     * Accessor method. Resets parent project in the process.
     */
    @Override public void setParentModule(DefaultModule module) {
        this.parentModule = module;
        if (module != null) {
            setParentProject(module.getParentProject());
        }
    }

    /**
     * Adds an existing component to the children group of modules
     *
     * @param component any (if null no operation performed)
     * @return any
     */
    @Override public final DefaultModule addComponent(DefaultModule component) {
        if (component != null) {
            if (components == null) {
                components = new HashSet<DefaultModule>();
            }
            components.add(component);
            component.setParentModule(this);
        }
        return component;
    }

    /**
     * Accessor method
     *
     * @return a non null unmodifiable collection
     */
    @Override public Collection<DefaultModule> getComponents() {
        return CollectionUtils.isEmpty(components) ? Collections.EMPTY_SET : Collections.unmodifiableSet(components);
    }

    /**
     * Clears the current collection of components and attaches a new one. All the components
     * are updated to reflect this new hierarchy.
     */
    public void setComponents(Set<DefaultModule> components) {
        if (CollectionUtils.isNotEmpty(this.components)) {
            this.components.clear();
        }
        this.components = components;
        if (components != null) {
            for (DefaultModule component : components) {
                component.setParentModule(this);
            }
        }
    }

    /**
     * Creates a valid component type and fills with default values.
     *
     * @param componentName any (if null or empty no operation is performed)
     * @param componentDescription any (
     * @return any
     */
    @Override public final DefaultModule createComponent(String componentName, String componentDescription) {
        DefaultModule component = null;
        if (hasText(componentName) && hasText(componentDescription)) {
            component = new DefaultModule();
            component.setModuleName(componentName);
            component.setDescription(componentDescription);
            addComponent(component);
        }
        return component;
    }

    /**
     * Invoked by the persistence provider to add entity security
     * automatically. Avoid calling this method.
     */
    @PrePersist
    protected final void setSecurity() {
        setPublicView(getParentProject().isPublicView());
        for (UserImpl user : getParentProject().getOwners()) {
            addOwner(user);
        }
        if (!isPublicView()) {
            for (UserImpl user : getParentProject().getViewers()) {
                addViewer(user);
            }
        }
    }

    /**
     * A simple module representation in the form of "module: name"
     *
     * @return a not empty string
     */
    @Override public final String toString() {
        return "module: " + getModuleName();
    }

}
